﻿#region Header
// Copyright 2007-2011, Jerónimo Milea
// 
// This file is part of NetJsWire.
// 
//     NetJsWire is free software: you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.
// 
//     NetJsWire is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.
// 
//     You should have received a copy of the GNU General Public License
//     along with NetJsWire.  If not, see <http://www.gnu.org/licenses/>.
#endregion
#region Using

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using System.Xml;
using NetJsWire.JsonRpcHandler;
using NetJsWire.JsonRpcHandler.Attributes;
using NetJsWire.Util;
using NetJsWire.Widgets.Core;
using NetJsWire.Widgets.Data;
using NetJsWire.Widgets.Renderers.Core;
using NetJsWire.Widgets.Ui.Table;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Formatting = Newtonsoft.Json.Formatting;

#endregion

namespace NetJsWire.Widgets.Renderers.Table
{
    [ClassName( "Ext.grid.GridPanel" )]
    internal class Table : Widget
    {
        // ReSharper disable InconsistentNaming
        protected const string _AFTEREDIT_CLIENT_NAME = "afteredit";
        protected const string _ROWDOUBLECLICK_CLIENT_NAME = "rowdblclick";
        protected const string _CELLDOUBLECLICK_CLIENT_NAME = "celldblclick";
        protected const string _COLUMNHIDDENCHANGED_CLIENT_NAME = "hiddenchange";
        protected const string _KEYPRESS_CLIENT_NAME = "keypress";
        protected const string _CLIENTKEYPRESS_CLIENT_NAME = "keypress";
        protected const string _CELLDOUBLECLICK = "function(grid, ri, ci, evt) {{ SendToServer('{1}', 'CellDoubleClick', '{1}', [ri, ci]); }}";
        protected const string _COLUMNHIDDENCHANGED = "function(cm, ci, v) {{ SendToServer('{1}', 'ColumnHiddenChanged', '{1}', [ci, v] ); }}";
        protected const string _KEYPRESS_BASE = "function(evt) {{ if(evt.getKey() == |key| {{evt.stopEvent(); SendToServer(\"{0}\", \"ClientKeyPress\", \"{1}\", evt.getKey(), false, true); }} }}";
        protected const string _ROWDOUBLECLICK = "function(grid, ri, evt) {{ SendToServer('{1}', 'RowDoubleClick', '{1}', null); }}";
        // ReSharper restore InconsistentNaming

        private const string UpdateCellTpl = "{0}.updateCell('{1}',\"{2}\",{3});";
        private readonly Ui.Table.Table _grid;
        private readonly List<int> _rowsRendered = new List<int>();
        private readonly Dictionary<int, int> _rowsToRemove = new Dictionary<int, int>();
        protected string PendingChanges = string.Empty;
        protected string _AFTEREDIT = "function(evt) {{ SendToServer(\"{1}\", \"Afteredit\", \"{1}\", [evt.record.get(\"RI\"), evt.column, evt.value]); }}";
        protected string _CLIENTKEYPRESS = string.Empty;
        protected string _CUSTOM = string.Empty;
        protected string _KEYPRESS = string.Empty;
        protected string _SELECTIONCHANGE = "function(sm, ri, r) {{ if(sm.hasSelection()) {{SendToServer('{1}', 'SelectionChange', '{1}', [r.get('RI'),0], true );}} }}";
        protected string _SELECTIONCHANGE_CLIENT_NAME = "rowselect";
        protected string _SORTREQUEST = "";
        private int _clientIndex;
        private XmlNode _columnsNode;
        private string _keys = string.Empty;
        private bool _needsReconfigure;
        private bool _requestingData;
        private bool _rowsCleared;
        private XmlNode _selectionModelNode;
        private string _sortColumn = string.Empty;
        private string _sortDir = string.Empty;
        private int _start;

        public Table( object baseObject )
            : base( baseObject )
        {
            _grid = ( Ui.Table.Table ) baseObject;
        }

        private XmlNode GridDataNode
        {
            get
            {
                if ( ControlNode[ "griddata" ] == null )
                    ControlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                             "griddata", "" ) );

                return ControlNode[ "griddata" ];
            }
        }

        public int Limit
        {
            get { return _grid.PageSize; }
        }

        private void GridDataRequestedFromClient( object sender, EventArgs e )
        {
            //int limit;
            WebApplication.Instance.OnlyGridData = true;
            WebApplication.Instance.IdGridRequesting = InternalID;
            _requestingData = true;
            var parameters = ( NameValueCollection ) sender;
            if ( !int.TryParse( parameters[ "start" ], out _start ) )
                _start = 0;
            //if ( !int.TryParse( parameters[ "limit" ], out limit ) )
            //    limit = Limit;
            var oldSort = _sortColumn;
            var oldDir = _sortDir;
            _sortColumn = parameters[ "sort" ];
            _sortDir = parameters[ "dir" ];

            try
            {
                if ( !string.IsNullOrEmpty( _sortColumn ) && _sortColumn != oldSort || _sortDir != oldDir )
                {
                    if ( Base.HasSortRequestHandled )
                    {
                        Base.HandleSortRequest( _sortColumn, _sortDir );
                    }
                    else
                    {
                        foreach (
                            var col in
                                ( ( Ui.Table.Table ) Base ).Columns.Cast<DataGridViewColumn>().Where(
                                                                                                      c =>
                                                                                                      c.DataPropertyName ==
                                                                                                      _sortColumn ) )
                        {
                            Base.Sort( col,
                                       _sortDir == "ASC" ? ListSortDirection.Ascending : ListSortDirection.Descending );
                            break;
                        }
                    }
                }
            }
            catch ( InvalidOperationException )
            {
            }

            RenderData();
            SetAttribute( ControlNode[ "griddata" ], "mode", "JSONdata" );
            _requestingData = false;
            return;
        }

        public override void Render()
        {
            if ( BaseParent is IWidgetContainer )
            {
                SetAttribute( ControlNode, "isInContainer", true );
                SetConstructionProperty( "isInContainer", true );
            }
            else
            {
                SetStyleProperty( "position", "absolute" );
                SetConstructionProperty( "renderTo", ClientParentToRender );
            }

            EnsureParentNodePosition();

            _columnsNode =
                ControlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element, "columns",
                                                                                         "" ) );
            _columnsNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element, "events", "" ) );

            _selectionModelNode =
                ControlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                         "selectionModel", "" ) );
            _selectionModelNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                            "events", "" ) );

            if ( !Rendered )
            {
                if ( !_grid.ReadOnly )
                {
                    ClassName = "Ext.grid.EditorGridPanel";
                    _SELECTIONCHANGE =
                        "function(sm, ri, ci) {{ if(sm.hasSelection()) {{SendToServer('{0}', 'SelectionChange', '{1}', [sm.selection.record.get('RI'),ci], true );}} }}";
                    _SELECTIONCHANGE_CLIENT_NAME = "cellselect";

                    _grid.SelectionModel = SelectionModel.CellSelectionModel;
                }
                else
                {
                    _AFTEREDIT = string.Empty;
                }

                SetAttribute( ControlNode, "SelectionModel", _grid.SelectionModel.ToString() );

                SetConstructionProperty( "tabIndex", ( ( Ui.Table.Table ) Base ).TabIndex );

                if ( _grid.Frame )
                    SetConstructionProperty( "frame", _grid.Frame );

                if ( !string.IsNullOrEmpty( _grid.AutoExpandColumn ) && _grid.ForceFit )
                    SetConstructionProperty( "autoExpandColumn", _grid.AutoExpandColumn );

                if ( !string.IsNullOrEmpty( _grid.GroupColumn ) )
                {
                    SetAttribute( ControlNode, "groupBy", _grid.GroupColumn );
                    SetAttribute( ControlNode, "groupTextTpl", _grid.GroupTextTemplate );
                    SetAttribute( ControlNode, "sortInfo", _grid.GroupTextTemplate );
                }

                if ( _grid.UseGroupSummary )
                {
                    AddPrecode( string.Format( "var summary_{0}=new Ext.grid.GroupSummary();", InternalID ) );
                    Plugins.Add( string.Format( "summary_{0}", InternalID ) );
                }

                if ( _grid.UseGridSummary )
                {
                    AddPrecode( string.Format( "var gridSummary_{0}=new Ext.ux.grid.GridSummary();", InternalID ) );
                    Plugins.Add( string.Format( "gridSummary_{0}", InternalID ) );
                }

                if ( _grid.UseRowExpander )
                {
                    AddPrecode(
                               string.Format(
                                             "var expander_{0} = new Ext.grid.RowExpander({{tpl : new Ext.Template({1}) }});",
                                             InternalID, _grid.RowExpanderTemplate ) );

                    Plugins.Add( string.Format( "expander_{0}", InternalID ) );
                }

                if ( !string.IsNullOrEmpty( _grid.Title ) )
                    SetConstructionProperty( "title", _grid.Title );

                if ( _grid.RemoteLoad )
                    _grid.DataRequestedFromClient += GridDataRequestedFromClient;

                if ( !_grid.RemoteLoad )
                    RenderData();

                if ( _grid.RemoteLoad )
                {
                    SetAttribute( ControlNode, "remoteLoad", true );
                    SetAttribute( ControlNode, "pageSize", _grid.PageSize );
                    if ( _grid.RemoteSort )
                    {
                        SetAttribute( ControlNode, "remoteSort", true );
                        SetConstructionProperty( "remoteSort", true );
                    }
                }

                if ( _grid.DataSource is IDataStore )
                {
                    SetAttribute( ControlNode, "customStore", true );
                    SetAttribute( ControlNode, "storeName", ( ( IWidget ) _grid.DataSource ).Renderer.InternalID );
                }

                if ( _grid.ForceFit )
                    SetAttribute( ControlNode, "forceFit", true );

                if ( !_grid.EnableColumnHide )
                    SetConstructionProperty( "enableColumnHide", false );

                if ( _grid.UseTopBar && _grid.TopBar != null )
                {
                    SetAttribute( "hasTBar", true );
                    var oldNode = _grid.TopBar.Renderer.ControlNode.CloneNode( true );
                    //_grid.Height -= _grid.TopBar.Height;
                    _grid.TopBar.Renderer.ControlNode.ParentNode.RemoveChild( _grid.TopBar.Renderer.ControlNode );
                    ControlNode[ "controls" ].AppendChild(
                                                          WebApplication.Instance.XmlDocument.CreateNode(
                                                                                                         XmlNodeType.
                                                                                                             Element,
                                                                                                         "tbar", "" ) );
                    ControlNode[ "controls" ][ "tbar" ].AppendChild( oldNode );
                }

                if ( _grid.UseBbar && _grid.BottomBar != null )
                {
                    SetAttribute( "hasbBar", true );
                    var oldNode = _grid.BottomBar.Renderer.ControlNode.CloneNode( true );
                    //_grid.Height -= _grid.BottomBar.Height;
                    _grid.BottomBar.Renderer.ControlNode.ParentNode.RemoveChild( _grid.BottomBar.Renderer.ControlNode );
                    ControlNode[ "controls" ].AppendChild(
                                                          WebApplication.Instance.XmlDocument.CreateNode(
                                                                                                         XmlNodeType.
                                                                                                             Element,
                                                                                                         "bbar", "" ) );
                    ControlNode[ "controls" ][ "bbar" ].AppendChild( oldNode );
                }

                RenderColumns();

                RenderView();

                //string plugins = JsonConvert.ExportToString( Plugins );
                //SetConstructionProperty( "plugins", plugins.Replace( "\"", "" ) );

                if ( !string.IsNullOrEmpty( _grid.View.EmptyText ) )
                {
                    if ( _grid.RemoteLoad )
                    {
                        WebApplication.Instance.AddSingleChange( string.Format( "{0}.reloadData(0, {1});", GetCmp, Limit ) );
                    }
                    else
                    {
                        if ( _grid.Rows.Count <= 0 )
                            WebApplication.Instance.AddSingleChange( string.Format( "{0}.getView().refresh();", GetCmp ) );
                    }
                }

                _grid.DataSourceChanged += GridDataSourceChanged;
                _grid.RowsAdded += GridRowsAdded;
                _grid.RowsRemoved += GridRowsRemoved;
                _grid.CurrentCellDirtyStateChanged += GridCurrentCellDirtyStateChanged;
                _grid.CellValueChanged += GridCellValueChanged;
                _grid.ColumnAdded += GridColumnAdded;
                _grid.ColumnRemoved += GridColumnRemoved;
                _grid.Rows.CollectionChanged += RowsCollectionChanged;

                if ( !_grid.TrackSelectionChanges )
                {
                    _SELECTIONCHANGE = string.Empty;
                }
            }
            else
            {
                if ( !string.IsNullOrEmpty( PendingChanges ) )
                {
                    if ( _needsReconfigure )
                    {
                        Reconfigure( false );
                    }
                    if ( !_grid.RemoteLoad )
                    {
                        RenderData();
                        if ( _grid.ReadOnly )
                            ControlNode[ "code" ].LastChild.InnerText +=
                                string.Format( "{0}.getSelectionModel().selectFirstRow();", GetCmp );
                    }
                    PendingChanges = string.Empty;
                    _rowsRendered.Clear();
                }
                else if ( _needsReconfigure )
                {
                    Reconfigure( true );
                }
            }

            base.Render();

            _rowsCleared = false;
            _rowsToRemove.Clear();
        }

        private void GridCurrentCellDirtyStateChanged( object sender, EventArgs e )
        {
            if ( _grid.IsCurrentCellDirty )
            {
                //_grid.CommitEdit( DataGridViewDataErrorContexts.Commit );
            }
        }

        private void GridColumnAdded( object sender, DataGridViewColumnEventArgs e )
        {
            if ( _grid.Disposing )
            {
                return;
            }
            _needsReconfigure = true;
        }

        private void Reconfigure( bool includeData )
        {
            SetAttribute( ControlNode, "reconfigure", true );
            if ( !string.IsNullOrEmpty( _grid.GroupColumn ) )
            {
                SetAttribute( ControlNode, "groupBy", _grid.GroupColumn );
                SetAttribute( ControlNode, "groupTextTpl", _grid.GroupTextTemplate );
                SetAttribute( ControlNode, "sortInfo", _grid.GroupTextTemplate );

                if ( _grid.GroupsStartCollapsed )
                    SetAttribute( ControlNode, "startCollapsed", true );
            }

            if ( _grid.UseGroupSummary )
                SetAttribute( ControlNode, "useGroupSummary", true );

            RenderColumns();

            if ( includeData )
            {
                RenderData();
                Debug.WriteLine( "Reconfigured with data." );
            }
            else
            {
                ControlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element, "griddata",
                                                                                         "" ) ).InnerText = "[]";
                Debug.WriteLine( "Reconfigured." );
            }

            _needsReconfigure = false;
        }

        private void GridDataSourceChanged( object sender, EventArgs e )
        {
            //if ( _currencyManager != null )
            //{
            //    _currencyManager.ItemChanged -= CurrencyManagerItemChanged;
            //}

            //_currencyManager = null;

            //if ( _grid.DataSource != null )
            //{
            //    if ( _grid.BindingContext != null && _grid.BindingContext[ _grid.DataSource ] != null )
            //    {
            //        _currencyManager = ( CurrencyManager ) _grid.BindingContext[ _grid.DataSource ];

            //        _currencyManager.ItemChanged += CurrencyManagerItemChanged;
            //    }
            //}

            if ( _grid.RemoteLoad )
            {
                if ( !_requestingData )
                    WebApplication.Instance.AddSingleChange( string.Format( "{0}.reloadData(0, {1});", GetCmp, Limit ) );
                //WebApplication.Instance.AddSingleChange( string.Format( "{0}.getStore().reload({{params:{{start:0, _limit:{1} }} }});", GetCmp, _limit ) );
            }
            else
            {
                try
                {
                    RenderData();
                }
                catch
                {
                }
            }
        }

        private void RenderView()
        {
            XmlNode viewNode;
            if ( ControlNode[ "view" ] == null )
                viewNode =
                    _columnsNode =
                    ControlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element, "view",
                                                                                             "" ) );
            else
                viewNode = ControlNode[ "view" ];

            var viewType = _grid.View.GetType();

            SetAttribute( ControlNode, "viewTypeName", viewType.Name );

            var pis = viewType.GetProperties();
            foreach ( var pi in pis )
            {
                var oValue = pi.GetValue( _grid.View, null );


                var atts = pi.GetCustomAttributes( typeof( DefaultValueAttribute ), true );
                if ( atts.Length > 0 )
                {
                    var dAtt = atts[ 0 ] as DefaultValueAttribute;
                    //We only set the value if it is different than default.
                    if ( dAtt != null )
                    {
                        if ( ( oValue != null && !oValue.Equals( dAtt.Value ) ) ||
                             ( dAtt.Value != null && !dAtt.Value.Equals( oValue ) ) )
                        {
                            var propName = pi.Name.Substring( 0, 1 ).ToLower() + pi.Name.Substring( 1 );
                            if ( propName != "groupTextTpl" && propName != "groupRenderer" )
                                SetAttribute( viewNode, propName, JsonConvert.SerializeObject( oValue ) ); //JsonConvert.ExportToString( oValue ).Replace( "[", "" ).Replace( "]", "" ) );
                            else
                                SetAttribute( viewNode, propName, oValue );
                        }
                    }
                }
                else
                {
                    var propName = pi.Name.Substring( 0, 1 ).ToLower() + pi.Name.Substring( 1 );
                    SetAttribute( viewNode, propName, JsonConvert.SerializeObject( oValue ) );
                }
            }

            SetAttribute( viewNode, "getEditorParent", "function() {return this.mainWrap.dom; }" );
        }

        private void RenderColumns()
        {
            if ( ControlNode[ "columns" ] == null )
            {
                _columnsNode =
                    ControlNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                             "columns", "" ) );
            }
            else
            {
                _columnsNode.RemoveAll();
            }

            _columnsNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element, "events", "" ) );

            foreach ( DataGridViewColumn col in ( ( Ui.Table.Table ) Base ).Columns )
            {
                var colNode = _columnsNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "column", "" ) );
                SetAttribute( colNode, "name", !string.IsNullOrEmpty( col.DataPropertyName ) ? col.DataPropertyName : col.Name );
                SetAttribute( colNode, "header", col.HeaderText );
                SetAttribute( colNode, "width", col.Width );
                SetAttribute( colNode, "tooltip", col.ToolTipText );

                SetAttribute( colNode, "type", "text" );

                if ( _grid.SummaryTypes.ContainsKey( col.Index ) )
                {
                    SetAttribute( colNode, "summaryType", _grid.SummaryTypes[ col.Index ] );
                }
                if ( _grid.SummaryRenderers.ContainsKey( col.Index ) )
                {
                    SetAttribute( colNode, "summaryRenderer", _grid.SummaryRenderers[ col.Index ] );
                }
                if ( !string.IsNullOrEmpty( col.DefaultCellStyle.Format ) )
                {
                    //TODO: the only formats we support now are numeric, currency and date.
                    var format = col.DefaultCellStyle.Format;
                    if ( format.StartsWith( "N" ) && format.Length == 2 ) // Numeric format
                    {
                        SetAttribute( colNode, "renderer", "Ext.util.Format.numberRenderer(" + "'0.".PadRight( Convert.ToInt32( format.Substring( 1 ) ) + 3, '0' ) + "')" );
                        if ( !_grid.ReadOnly )
                            SetAttribute( colNode, "editor", "new Ext.form.NumberField({allowBlank: false,decimalPrecision:" + format.Substring( 1 ) + "})" );
                    }
                    else if ( format.StartsWith( "C" ) && format.Length == 2 ) // Currency format
                    {
                        SetAttribute( colNode, "renderer", "Ext.util.Format.usMoney" );
                        if ( !_grid.ReadOnly )
                            SetAttribute( colNode, "editor", "new Ext.form.NumberField({allowBlank: false})" );
                    }
                    else if ( format == "d" )
                    {
                        SetAttribute( colNode, "renderer", "Ext.util.Format.dateRenderer('d/m/Y')" );
                        if ( !_grid.ReadOnly )
                            SetAttribute( colNode, "editor",
                                          "new Ext.form.DateField({format: 'd/m/Y', minValue: '01/01/06'})" );
                    }
                }
                var css = "";
                if ( col is DataGridViewCheckBoxColumn )
                {
                    SetAttribute( colNode, "type", "checkBox" );

                    SetAttribute( colNode, "name",
                                  !string.IsNullOrEmpty( col.DataPropertyName ) ? col.DataPropertyName : col.Name );
                    SetAttribute( colNode, "colName",
                                  string.Format( "chBox_col_{0}",
                                                 !string.IsNullOrEmpty( col.DataPropertyName )
                                                     ? col.DataPropertyName
                                                     : col.Name ) );

                    var editable = ( !_grid.ReadOnly && !col.ReadOnly ) ? "true" : "false";


                    AddPrecode(
                               string.Format(
                                             "var chBox_col_{0} = new Ext.grid.CheckColumn({{ header:'{1}',dataIndex:'{2}',width:{3}, editable:{4} }});",
                                             !string.IsNullOrEmpty( col.DataPropertyName )
                                                 ? col.DataPropertyName
                                                 : col.Name,
                                             col.HeaderText, col.DataPropertyName, col.Width, editable ) );

                    if ( !_grid.ReadOnly && !col.ReadOnly )
                        Plugins.Add( string.Format( "chBox_col_{0}",
                                                    !string.IsNullOrEmpty( col.DataPropertyName )
                                                        ? col.DataPropertyName
                                                        : col.Name ) );
                }
                if ( col is INetJsWireGridColumn )
                {
                    if ( !string.IsNullOrEmpty( ( col as INetJsWireGridColumn ).Css ) )
                    {
                        css = ( col as INetJsWireGridColumn ).Css + ";";
                        SetAttribute( colNode, "css", css );
                    }

                    if ( !string.IsNullOrEmpty( ( col as INetJsWireGridColumn ).Id ) )
                    {
                        SetAttribute( colNode, "id", ( col as INetJsWireGridColumn ).Id );
                    }

                    if ( !( col as INetJsWireGridColumn ).HideAble )
                    {
                        SetAttribute( colNode, "hideable", false );
                    }

                    if ( !string.IsNullOrEmpty( ( col as INetJsWireGridColumn ).Editor ) )
                    {
                        SetAttribute( colNode, "editor", ( col as INetJsWireGridColumn ).Editor );
                    }
                    if ( !string.IsNullOrEmpty( ( col as INetJsWireGridColumn ).Renderer ) )
                    {
                        SetAttribute( colNode, "renderer", ( col as INetJsWireGridColumn ).Renderer );
                    }
                    if ( ( col as INetJsWireGridColumn ).Fixed )
                    {
                        SetAttribute( colNode, "fixed", ( col as INetJsWireGridColumn ).Fixed );
                    }

                    /*******************************************************************************************************/
                    /* ------- ComboBox columns are a special case, we need to create a Store and a ComboBox for it. ------*/
                    /*******************************************************************************************************/
                    if ( col is NetJsWireComboBoxColumn )
                    {
                        SetAttribute( colNode, "type", "combo" );

                        string storeId;
                        var dataCol = col as DataGridViewComboBoxColumn;

                        if ( dataCol.DataSource is Store )
                        {
                            storeId = ( dataCol.DataSource as Store ).Renderer.ClientID;

                            //we ensure that the store node is before the grid one in order to get the expected behaviour.
                            ( ( dataCol.DataSource as Store ).Renderer as Data.Store ).
                                MoveControlNodeBeforeTopLevelContainer();
                            //XmlNode oldNode = ( dataCol.DataSource as Widgets.Data.Store ).Renderer.ControlNode.CloneNode( true );
                            //WebApplication.Instance.XmlDocument[ "class" ][ "controls" ].RemoveChild( ( dataCol.DataSource as Widgets.Data.Store ).Renderer.ControlNode );
                            //WebApplication.Instance.XmlDocument[ "class" ][ "controls" ].InsertBefore( oldNode, ControlNode );
                        }
                        else
                        {
                            //TODO: Create a default store... need some thinking... do we need updates or we just simply change the store when we need?
                            storeId = null;
                        }

                        //ControlNode[ "precode" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) ).InnerText = "" +
                        AddPrecode( "var __grid_Edit_Combo_" + InternalID + "_" + col.Index +
                                    "= new Ext.form.ComboBox({" +
                                    "typeAhead: true," +
                                    "triggerAction: 'all'," +
                                    "displayField : '" + dataCol.DisplayMember + "'," +
                                    "valueField : '" + dataCol.ValueMember + "'," +
                                    "store:" + ( storeId ?? "null" ) + ", " +
                                    "lazyRender:true, " +
                                    "listClass: 'x-combo-list-small', " +
                                    "mode: 'local'" +
                                    "});" );
                        SetAttribute( colNode, "editor", "__grid_Edit_Combo_" + InternalID + "_" + col.Index );
                        SetAttribute( colNode, "renderer",
                                      "Ext.ux.comboBoxRenderer(__grid_Edit_Combo_" + InternalID + "_" + col.Index + ")" );
                    }
                }
                if ( col.DefaultCellStyle.Alignment == DataGridViewContentAlignment.BottomCenter ||
                     col.DefaultCellStyle.Alignment == DataGridViewContentAlignment.MiddleCenter ||
                     col.DefaultCellStyle.Alignment == DataGridViewContentAlignment.TopCenter )
                {
                    SetAttribute( colNode, "align", "center" );
                }
                if ( col.DefaultCellStyle.Alignment == DataGridViewContentAlignment.BottomRight ||
                     col.DefaultCellStyle.Alignment == DataGridViewContentAlignment.MiddleRight ||
                     col.DefaultCellStyle.Alignment == DataGridViewContentAlignment.TopRight )
                {
                    SetAttribute( colNode, "align", "right" );
                }
                if ( col.DefaultCellStyle.WrapMode == DataGridViewTriState.True )
                {
                    SetAttribute( colNode, "css", "white-space:normal;" + css );
                }
                if ( !col.Visible )
                {
                    SetAttribute( colNode, "hidden", true );
                }
                if ( !col.ReadOnly && !_grid.ReadOnly )
                {
                    SetAttribute( colNode, "editable", true );
                    if ( colNode.Attributes[ "editor" ] == null && !( col is NetJsWireComboBoxColumn ) )
                        SetAttribute( colNode, "editor", "{xtype:'textfield', allowBlank:false}" );
                }
                else if ( col.ReadOnly || _grid.ReadOnly )
                {
                    SetAttribute( colNode, "editable", false );
                }
                if ( _grid.CustomRenderers.ContainsKey( col.Index ) )
                {
                    SetAttribute( colNode, "renderer", _grid.CustomRenderers[ col.Index ] );
                }
                if ( col.SortMode == DataGridViewColumnSortMode.Programmatic ||
                     col.SortMode == DataGridViewColumnSortMode.Automatic )
                {
                    SetAttribute( colNode, "sortable", true );
                }
            }
        }

        private void GridColumnRemoved( object sender, DataGridViewColumnEventArgs e )
        {
            if ( _grid.Disposing )
            {
                return;
            }

            _needsReconfigure = true;
        }

        private void RowsCollectionChanged( object sender, CollectionChangeEventArgs e )
        {
            if ( _grid == null || _grid.Parent == null || _grid.Disposing || _grid.Parent.Disposing )
            {
                return;
            }

            if ( e.Action == CollectionChangeAction.Refresh && !_grid.RemoteLoad )
            {
                if ( !_rowsCleared )
                {
                    //ControlNode[ "precode" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element", "codeline", "" ) )
                    //    .InnerText = string.Format( "try{{ {0}.getStore().removeAll(); }}catch(e){{}};", GetCmp );
                    //WebApplication.Instance.HasChanges = true;

                    WebApplication.Instance.AddSinglePreCodeChange(
                                                                   string.Format(
                                                                                 "try{{if({0}!=null){{ var __st = {0}.getStore(); if(__st!=null){{__st.removeAll();}} }} }}catch(e){{}};",
                                                                                 GetCmp ) );

                    //_grid._clientRowIndexes.Clear();
                    //_clientIndex = 0;

                    _rowsCleared = true;
                }
            }
        }

        private void GridRowsRemoved( object sender, DataGridViewRowsRemovedEventArgs e )
        {
            if ( _grid == null || _grid.Parent == null || _grid.Disposing || _grid.Parent.Disposing )
            {
                return;
            }

            if ( _grid.RemoteLoad )
            {
                if ( !_requestingData && e.RowCount > 0 )
                    WebApplication.Instance.AddSingleChange( string.Format( "{0}.reloadData(0, {1});", GetCmp, Limit ) );
                //WebApplication.Instance.AddSingleChange( string.Format( "{0}.getStore().reload({{params:{{start:0, _limit:{1} }} }});", GetCmp, _limit ) );
            }
            else
            {
                if ( e.RowCount > 0 )
                {
                    try
                    {
                        if ( !( _rowsToRemove.ContainsKey( e.RowIndex ) && _rowsToRemove[ e.RowIndex ] == e.RowCount ) )
                        {
                            for ( var i = e.RowIndex; i < e.RowIndex + e.RowCount; i++ )
                            {
                                if ( _grid.ClientRowIndexes.ContainsValue( i ) )
                                {
                                    var i1 = i;
                                    var node =
                                        GridDataNode.SelectSingleNode( "descendant::datarow[@ID='" +
                                                                       _grid.ClientRowIndexes.First( c => c.Value == i1 )
                                                                           .Key + "']" );
                                    if ( node != null )
                                    {
                                        GridDataNode.RemoveChild( node );
                                    }
                                }
                            }
                        }
                    }
                    catch
                    {
                        // Ignore
                    }
                    //CallCommand( "ActualizarRI", _grid.RowCount );

                    // Clear client index
                    for ( var i = e.RowIndex; i < e.RowIndex + e.RowCount; i++ )
                    {
                        if ( _grid.ClientRowIndexes.ContainsValue( i ) )
                        {
                            var i1 = i;
                            var key = _grid.ClientRowIndexes.First( c => c.Value == i1 ).Key;
                            _grid.ClientRowIndexes.Remove( key );
                        }
                    }

                    UpdateClientIndexes();

                    if ( ( _rowsToRemove.ContainsKey( e.RowIndex ) && _rowsToRemove[ e.RowIndex ] == e.RowCount ) ||
                         ( _rowsCleared && _grid.RowCount <= 0 ) )
                    {
                        return;
                    }

                    ControlNode[ "precode" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element",
                                                                                                          "codeline", "" ) )
                        .InnerText = string.Format( "{0}.removeRows({1},{2});", GetCmp, e.RowIndex, e.RowCount );
                    _rowsToRemove.Add( e.RowIndex, e.RowCount );
                }
                else
                {
                    if ( !_rowsCleared )
                    {
                        ControlNode[ "precode" ].AppendChild(
                                                             WebApplication.Instance.XmlDocument.CreateNode(
                                                                                                            XmlNodeType.
                                                                                                                Element,
                                                                                                            "codeline",
                                                                                                            "" ) ).
                            InnerText =
                            string.Format( "try{{ {0}.getStore().removeAll(); }}catch(e){{}};", GetCmp );
                        _rowsCleared = true;
                    }
                }
            }
            WebApplication.Instance.HasChanges = true;
        }

        private void UpdateClientIndexes()
        {
            var orderedClientIndexes =
                _grid.ClientRowIndexes.OrderBy( c => c.Value );

            var index = 0;
            foreach ( var item in orderedClientIndexes )
            {
                _grid.ClientRowIndexes[ item.Key ] = index++;
            }
        }

        private void GridRowsAdded( object sender, DataGridViewRowsAddedEventArgs e )
        {
            if ( _grid == null || _grid.Parent == null || _grid.Disposing || _grid.Parent.Disposing )
            {
                return;
            }

            _rowsCleared = false;
            if ( _grid.RemoteLoad )
            {
                if ( !_requestingData )
                    WebApplication.Instance.AddSingleChange( string.Format( "{0}.reloadData(0, {1});", GetCmp, Limit ) );

                //WebApplication.Instance.AddSingleChange( string.Format( "{0}.getStore().reload({{params:{{start:0, _limit:{1} }} }});", GetCmp, _limit ) );
                return;
            }
            // Pasamos RenderData() porque borra los registros antes de mostrar las filas.
            //RenderData();
            RenderRows( e.RowIndex, e.RowCount );
        }

        private void GridCellValueChanged( object sender, DataGridViewCellEventArgs e )
        {
            if ( _grid == null || _grid.Parent == null || _grid.Disposing || _grid.Parent.Disposing )
            {
                return;
            }

            //if ( _grid.internalChange )
            //    return;
            //_rowsCleared = false;
            //TODO: finnish this
            if ( !string.IsNullOrEmpty( PendingChanges ) )
                Render();
            if ( e.RowIndex >= 0 )
            {
                var value = _grid.Rows[ e.RowIndex ].Cells[ e.ColumnIndex ].Value;
                if ( _grid.IsInternalChange && _grid.InternalChange.Row == e.RowIndex &&
                     _grid.InternalChange.Col == e.ColumnIndex && _grid.InternalChange.Value == value )
                {
                    return;
                }

                if ( value is DateTime )
                {
                    var dt = ( DateTime ) value;
                    var val = JsonConvert.SerializeObject( dt );
                    ControlNode[ "precode" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element",
                                                                                                          "codeline",
                                                                                                          "" ) ).
                        InnerText
                        =
                        string.Format( UpdateCellTpl, GetCmp,
                                       _grid.ClientRowIndexes.First( c => c.Value == e.RowIndex ).Key,
                                       !string.IsNullOrEmpty( _grid.Columns[ e.ColumnIndex ].DataPropertyName )
                                           ? _grid.Columns[ e.ColumnIndex ].DataPropertyName
                                           : _grid.Columns[ e.ColumnIndex ].Name, val );
                }
                else
                {
                    ControlNode[ "precode" ].AppendChild( WebApplication.Instance.XmlDocument.CreateNode( "element",
                                                                                                          "codeline", "" ) )
                        .InnerText
                        =
                        string.Format( UpdateCellTpl, GetCmp,
                                       _grid.ClientRowIndexes.First( c => c.Value == e.RowIndex ).Key,
                                       !string.IsNullOrEmpty( _grid.Columns[ e.ColumnIndex ].DataPropertyName )
                                           ? _grid.Columns[ e.ColumnIndex ].DataPropertyName
                                           : _grid.Columns[ e.ColumnIndex ].Name,
                                        JsonConvert.SerializeObject( value ) );
                }
            }
            else
            {
                return;
            }
        }

        public void AddKeyPressEventHandler( int key )
        {
            _CLIENTKEYPRESS = string.Empty;
            if ( string.IsNullOrEmpty( _keys ) )
            {
                _keys = key + ")";
            }
            else
            {
                _keys += " || (evt.getKey() == " + key + ")";
            }
            _CLIENTKEYPRESS = _KEYPRESS_BASE.Replace( "|key|", _keys );
            if ( Events.Contains( "ClientKeyPress" ) )
                Events.Remove( "ClientKeyPress" );
        }

        public void RenderData()
        {
            _grid.ClientRowIndexes.Clear();
            _clientIndex = 0;
            GridDataNode.RemoveAll();

            RenderRows( 0 + ( _grid.RemoteLoad ? _start : 0 ), ( _grid.RemoteLoad ? Limit : _grid.Rows.Count ) );
        }

        private void RenderRows( int rowIndex, int rowCount )
        {
            if ( !_grid.RemoteLoad )
                SetAttribute( ControlNode, "appendData", _grid.Rows.Count > rowCount );

            SetAttribute( GridDataNode, "totalCount", _grid.Rows.Count );

            for ( var i = rowIndex; i < ( rowIndex + rowCount ) && i < _grid.Rows.Count; i++ )
            {
                var drv = _grid.Rows[ i ];

                var rowNode =
                    GridDataNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element,
                                                                                              "datarow", "" ) );

                RenderRow( drv, rowNode );
            }
            AddCommand( "UpdateIndexes" );
        }

        private void RenderRow( DataGridViewRow drv, XmlNode rowNode )
        {
            //RI = Row Index
            var colNode =
                rowNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element, "datacol", "" ) );

            // here we establish the RI value
            SetAttribute( colNode, "name", "RI" );
            colNode.InnerText = _clientIndex.ToString();

            SetAttribute( rowNode, "ID", _clientIndex );

            // Here we add the client index to our internal Dictionary and increment the _clientIndex by 1
            _grid.ClientRowIndexes.Add( _clientIndex++, drv.Index );

            foreach ( DataGridViewColumn col in _grid.Columns )
            {
                var val = JsonConvert.SerializeObject( drv.Cells[ col.Name ].Value,
                                                                          Formatting.None,
                                                                          new JsonSerializerSettings
                                                                              {
                                                                                  Converters =
                                                                                      {
                                                                                          new JavaScriptDateTimeConverter
                                                                                              ()
                                                                                      },
                                                                                  ReferenceLoopHandling =
                                                                                      ReferenceLoopHandling.Ignore
                                                                              } );
                colNode =
                    rowNode.AppendChild( WebApplication.Instance.XmlDocument.CreateNode( XmlNodeType.Element, "datacol",
                                                                                         "" ) );
                SetAttribute( colNode, "name",
                              !string.IsNullOrEmpty( col.DataPropertyName ) ? col.DataPropertyName : col.Name );
                colNode.InnerText = val;
            }
        }
    }
}